home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 12 - 1996 / 12.01 Jan 96 / BeBox Folder / BeBox Snippets
Encoding:
Text File  |  1995-12-14  |  24.5 KB  |  674 lines  |  [TEXT/R*ch]

  1. This is an excerpt of an article from MacTech Magazine, January 1996.
  2. Copyright 1996, Xplain Corp.
  3.  
  4. Let’s take a look at some code, starting with the main() function:
  5.  
  6. main(int argc, char* argv[])
  7. {
  8.     // to randomize the movement of the bouncing ball
  9.     srand((long) sys_time());
  10.  
  11.     // make the new application object and start it running
  12.     my_app = new TBounceApp();
  13.     my_app->Run();
  14.  
  15.     // application is finished so cleanup and return
  16.     delete my_app;
  17.     return 0;
  18. }
  19.  
  20. The basic idea is to create the application object and start it running.  The
  21. Run() function returns when the application quits.  The TBounceApp object is then
  22. deleted and the program returns.  Most applications on the BeBox will have a
  23. similar main() function.
  24. The application object
  25. The BApplication object is the real launching point for most applications, and
  26. BeBounce is no exception.  Here is the definition of the TBounceApp class:
  27.  
  28.  
  29.  
  30. class TBounceApp : public BApplication {
  31. public:
  32.                                     TBounceApp();
  33. virtual                        ~TBounceApp();
  34.  
  35. virtual    void                MessageReceived(BMessage *msg);
  36.                 void                InitPartner(thread_id tid);
  37.                 void                RemovePartner();
  38.                 bool                SendToPartner(BMessage *msg);
  39.                 bool                SendPosition(BRect rect);
  40.  
  41. private:
  42.                 TWindow        *fWindow;
  43.                 BMessenger    *fPartner;
  44. };
  45.  
  46. The application constructor does several interesting things.  First, when the
  47. application is launched it needs to determine how many instances of the BeBounce
  48. application are already running.  Here is a portion of that function:
  49.  
  50. TBounceApp::TBounceApp()
  51.     : BApplication(MY_SIGNATURE)
  52. {
  53.     ...        // some initialization code
  54.  
  55.     /*
  56.      This version of BeBounce only supports 2 instances of the application
  57. running at 
  58.      the same time.  So if there are already 2 instances running force a QUIT. */
  59.     BList list;
  60.     be_roster->GetThreadList(MY_SIGNATURE, &list);
  61.     long app_count = list.CountItems();
  62.     if (app_count > 2) {
  63.         // only support 2 applications at a time
  64.         PostMessage(QUIT_REQUESTED);
  65.         return;
  66.     }
  67.     
  68.     ...    // more initialization to be discussed below
  69. }
  70.  
  71. The above code uses the be_roster (aka “rooster”) object, a system-wide object
  72. that, among other things, maintains information on all running applications.  The
  73. roster returns a list of all running applications that have the signature
  74. MY_SIGNATURE.  If there are more than two instances of the application already
  75. running, this instance simply quits.
  76. If this is the first instance of the application it simply creates the window:
  77.     
  78.     if (app_count == 1) {
  79.         // The first instance of BeBounce will have a ball.
  80.         fWindow = new TWindow(wpos, "Bouncing 1", TRUE);
  81.     } else {
  82.         ... // the second instance of BeBounce
  83.     }
  84.  
  85. If there are two instances, things are trickier:
  86.     
  87.     if (app_count == 1) {
  88.         ...    // the first instance of BeBounce
  89.     } else {
  90.         // This is the second instance of the BeBounce app
  91.         fWindow = new TWindow(wpos, "Bouncing 2", FALSE);
  92.  
  93.         ...    // determine which of the 2 instances is myself and which is the
  94. partner.
  95.  
  96.         // tid is the “thread id” of our partner!
  97.         InitPartner(tid);
  98.  
  99.  
  100.  
  101.  
  102.         /* Send the introductory message along with my thread
  103.          id and location in screen coordinates. */
  104.         BMessage *msg = new BMessage(BB_HELLO);
  105.         msg->AddLong("thread", Thread());
  106.         msg->AddRect("rect", wpos);
  107.         SendToPartner(msg);
  108.     }
  109.  
  110. Here, as before, the application creates the window.  It then determines the
  111. thread id of its partner application and initializes a BMessenger object for
  112. communicating with the partner.  The code that creates the messenger is in the
  113. utility member function TBounceApp::InitPartner():
  114.  
  115. void TBounceApp::InitPartner(thread_id tid)
  116. {
  117.     if (fPartner)    // BB_HELLO race condition
  118.         return;
  119.  
  120.     // establish a ‘messenger’ as the link to our partner
  121.     fPartner = new BMessenger(MY_SIG, tid);
  122.     if (fPartner->Error() != NO_ERROR) {
  123.         delete fPartner;
  124.         fPartner = NULL;
  125.     } 
  126. }
  127.  
  128. It is important to note that this function handles the race condition surrounding
  129. the BB_HELLO message.  Imagine launching two instances of BeBounce at the same
  130. time.  The call to GetThreadList() could return two instances to each
  131. application.  In this case both applications will behave as the second instance
  132. and both will send BB_HELLO messages.  This will result in
  133. TBounceApp::InitPartner being called twice by each application.  In this
  134. particular application the only result would be a small memory leak of the first
  135. BMessenger object.  Understanding these types of race conditions is one of the
  136. critical aspects of designing software for the BeBox.  Note that the code does
  137. not handle three applications launching simultaneously.  In this case all three
  138. applications might decide that they are the “third wheel” and quit.
  139. After creating the messenger, the code constructs a BB_HELLO BMessage object. 
  140. The next step is to add the thread id of this application to the message.  The
  141. other application uses this data to construct its own BMessenger.  The window
  142. position is also added to the message so that the partner knows this window’s
  143. initial position.  The message is sent using the utility function TBounceApp::
  144. SendToPartner.  Other than error handling SendToPartner contains just one line:
  145.  
  146. bool TBounceApp::SendToPartner(BMessage *msg)
  147. {
  148.     ...    // error handling
  149.     fPartner->SendMessage(msg);
  150.     ...    // error handling
  151. }
  152. The final steps of the constructor are to create a “Stop Ball” menu item and show
  153. the window on screen:
  154.     
  155.     // inside the TBounceApp constructor
  156.     /*
  157.      A little bit of menu code.  Add a ‘Stop Ball’ menu item to the application
  158. menu 
  159.      (found in the Dock window) By default items in the “app” menu post messages
  160. to 
  161.      the application.  In this case the message should be “targeted” to the
  162. window, not 
  163.      the app.
  164.     */
  165.     BPopUpMenu    *menu = AppMenu();
  166.     BMenuItem    *item = new BMenuItem("Stop Ball",
  167.                     new BMessage(BB_STOP), 'K');
  168.     item->SetTarget(fWindow);
  169.     menu->AddItem(item);
  170.  
  171.     fWindow->Show();
  172.     // this is the end of the TBounceApp constructor
  173.  
  174. The protocol that the applications use to communicate with each other is most
  175. apparent in the TBounceApp:: MessageReceived() member function:
  176.  
  177. void TBounceApp::MessageReceived(BMessage *msg)
  178. {
  179.     switch (msg->what) {
  180.         case BB_HELLO:
  181.             ...    // a second BeBounce application is saying hello!
  182.             break;
  183.         case BB_GOODBYE:
  184.             ...    // our partner is quitting
  185.             break;
  186.         case BB_WINDOW_MOVED:
  187.             ...    // partner moved, and has given us its new position
  188.             break;
  189.         case BB_BALL:
  190.             ...    // we’ve just been given the ball
  191.             break;
  192.     }
  193. }
  194.  
  195. Receiving a BB_HELLO message means that a second instance of the application is
  196. introducing itself.  Here is the code for handling this message:
  197.         
  198.         case BB_HELLO:
  199.             if (fWindow->Lock()) {
  200.                 /*
  201.                  A new instance of BeBounce was just launched
  202.                  and sent us the introductory message.
  203.                 */
  204.                 InitPartner(msg->FindLong("thread"));
  205.     
  206.                 // Initialize our partner’s current location.
  207.                 pos = msg->FindRect("rect");
  208.                 fWindow->PartnerMoved(pos);
  209.         
  210.                 // Tell our new partner our current location.
  211.                 pos = fWindow->Frame();
  212.                 SendPosition(pos);
  213.                 fWindow->Unlock();
  214.             }
  215.             break;
  216.  
  217. This code initializes a BMessenger using TBounceApp::InitPartner() and places the
  218. thread id in the message.  The partner’s window position is also retrieved from
  219. the message and saved.  Finally the code determines the position of its window
  220. and sends that to the partner.  The communication link between the two
  221. applications is now open.
  222.  
  223. The next message is BB_GOODBYE.  This message is sent as BeBounce applications
  224. quit.  The code for responding to this message is quite simple:
  225.  
  226.         case BB_GOODBYE:
  227.             if (fWindow->Lock()) {
  228.                 // Our partner is quitting.
  229.                 RemovePartner();
  230.                 if (msg->HasBool("ball"))
  231.                     fWindow->AddBall();
  232.                 fWindow->Unlock();
  233.             }
  234.             break;
  235.  
  236. The partner is removed using the function TBounceApp::RemovePartner, the
  237. complement of the InitPartner function seen earlier.  In addition, if the
  238. quitting partner currently had the ball a new ball is added to this application’s
  239. window.  This ensures that there is always a bouncing ball.  The code that sends
  240. the BB_GOODBYE message is described in the section on the window. 
  241. The final two messages sent between the applications, BB_WINDOW_MOVED and
  242. BB_BALL, are status messages informing the partner that either the window moved
  243. or the ball has moved through the gap.  Here is the code, without further
  244. explanation:
  245.         
  246.         case BB_WINDOW_MOVED:
  247.             /*
  248.              Our partner is informing us that it moved.  This message is
  249. continually 
  250.              generated as the other window is being moved.  TWindow::PartnerMoved
  251.                  redraws the window to reflect the new position.
  252.             */
  253.             if (fWindow->Lock()) {
  254.                 pos = msg->FindRect("rect");
  255.                 fWindow->PartnerMoved(pos);
  256.                 fWindow->Unlock();
  257.             }
  258.             break;
  259.  
  260.         case BB_BALL:
  261.             // Our partner just passed us the ball.
  262.             if (fWindow->Lock()) {
  263.                 BPoint    speed = msg->FindPoint("speed");
  264.                 float    rel_loc = msg->FindFloat("rel_loc");
  265.                 fWindow->AddBall(rel_loc, speed);
  266.                 fWindow->Unlock();
  267.             }
  268.             break;
  269. The window object
  270. The next class of interest is TWindow, a subclass of BWindow.  In the BeBounce
  271. application the window is responsible for managing the ball and for informing the
  272. partner application of particular events (see description of
  273. TBounceApp::MessageReceived() above).  The window also presents the UI for this
  274. application so there is some description of how applications can create and
  275. manage UI on the BeBox.  In the Be operating system a BWindow object provides an
  276. area that can display and retain rendered images.  A BWindow by itself cannot
  277. draw, only BViews can draw.  However, a BView must belong to a window in order to
  278. draw.  These two classes work hand in hand.
  279. Here is a portion of the TWindow class definition:
  280.  
  281. class TWindow : public BWindow {
  282. public:
  283.                             TWindow(BRect frame, const char *title,
  284.                                                 bool with_ball);
  285. virtual                ~TWindow();
  286.  
  287. virtual    void        MessageReceived(BMessage *msg);
  288. virtual    bool        QuitRequested();
  289. virtual    void        FrameMoved(BPoint new_position);
  290.                 void        AddBall();
  291.                 void        AddBall(float rel_location, BPoint speed);
  292.                 ...    // a few more member functions
  293.  
  294. private:
  295.                 void        DrawOffScreen(BView *v);
  296.                 ...    // a couple more private member functions and then
  297.                     // some private data members
  298. };
  299. The TWindow constructor contains several code fragments worth discussing:
  300.  
  301. TWindow::TWindow(BRect frame, const char *title, bool ball)
  302.     : BWindow(frame, title, TITLED_WINDOW, NOT_RESIZABLE)
  303. {
  304.     ...    // some initialization removed
  305.  
  306.     if (ball)
  307.         AddBall();
  308.  
  309.     /*
  310.      The drawing takes place in the view fOffView that was added to the offscreen
  311.  
  312.      bitmap fBitmap.  In this way we’ll do the drawing offscreen and then just
  313. blit the 
  314.      result to the screen.
  315.     */
  316.  
  317.     fBitmap = new BBitmap(b, COLOR_8_BIT, TRUE);
  318.     fOffView = new BView(b, "", 0, WILL_DRAW);
  319.  
  320.     fBitmap->Lock();
  321.     fBitmap->AddChild(fOffView);
  322.     DrawOffScreen(fOffView);    // draw the initial contents offscreen
  323.     fBitmap->Unlock();
  324.  
  325.     /*
  326.      This view is added to the visible window.  Its only role is to blit the
  327. offscreen 
  328.      bitmap to the visible window.
  329.     */
  330.     fMainView = new TBitmapView(b, fBitmap);
  331.     AddChild(fMainView);
  332.  
  333.     ... // some initialization removed
  334. }
  335.  
  336. Here is the first look at how windows and views are created and used on the
  337. BeBox.  To get smooth animation TWindow uses an offscreen bitmap (a BBitmap
  338. object) and a basic BView object (fOffView) for the rendering.  It is in the
  339. context of this view that all the actual drawing occurs.  The graphics
  340. primitives, like BView::FillRect() and BView::FillArc() that create the effect of
  341. a ball bouncing off real walls, takes place in this offscreen view.  The last
  342. view created, an instance of the TBitmapView class, is simply the helper view
  343. that blits the bits from the offscreen bitmap into the onscreen window.  Since
  344. the TBitmapView class (a subclass of BView) is so simple here is all its code:
  345.  
  346. TBitmapView::TBitmapView(BRect frame, BBitmap *bitmap)
  347.     : BView(frame, "", FOLLOW_NONE, WILL_DRAW)
  348. {
  349.     /*
  350.      The only job of this view is to blit the offscreen bits into the onscreen
  351. view.
  352.     */
  353.     fBitmap = bitmap;
  354. }
  355.  
  356. void TBitmapView::Draw(BRect update)
  357. {
  358.     // blit the bitmap with source and dest rectangle ‘update’
  359.     DrawBitmap(fBitmap, update, update);
  360. }
  361.  
  362. As stated previously, a BWindow is a kind of BLooper, which in turn is a kind of
  363. BReceiver.  As such, a window runs in its own message loop and it can receive
  364. messages.  A BView is also a kind of BReceiver so it too can receive messages.  A
  365. message posted to a window (using PostMessage) can be “targeted” to either the
  366. window or a view contained within that window.  Because of the connection between
  367. windows and views, a view typically receives messages in the context of its
  368. window.  Said another way, the handling of messages targeted to a view occurs in
  369. the window’s thread.
  370. On the BeBox, user actions on the keyboard and mouse are turned into BMessages,
  371. called interface events.  However, these messages are not handled by
  372. MessageReceived(), like the BeBounce message BB_HELLO.  Instead, interface events
  373. are dispatched to a set of virtual functions corresponding to the action.  Here
  374. are a few of those functions:
  375.  
  376. BView::MouseDown()                // mouse down event in that view
  377. BView::KeyDown()                    // keydown event while that view was
  378.                                                 // the “focused” view
  379. BView::FrameResized                // the view changed size
  380. BWindow::FrameMoved()            // the window position moved
  381. BWindow::QuitRequested()        // click on close-box of window
  382.  
  383. It turns out that in BeBounce only two interface events are of interest, the
  384. FrameMoved and QuitRequested events.  The system repeatedly generates the
  385. FrameMoved event as a window is being dragged.  This event is of interest because
  386. as the window moves, so should the gap that exists between the partner windows. 
  387. Here is the code for responding to the FrameMoved event:
  388.  
  389. void TWindow::FrameMoved(BPoint new_pos)
  390. {
  391.     /*
  392.      As window is moved around the screen we inform our
  393.      partner of our new location.  Also update our gap.
  394.     */
  395.     fMyFrame = Frame();
  396.     if (my_app->SendPosition(fMyFrame))
  397.         WindowsMoved(fMyFrame, fPartnerFrame);
  398. }
  399.  
  400. This code gets the window’s current location and sends it to our partner so that
  401. our partner can update its gap.  TBounceApp::SendPosition() sends the
  402. BB_WINDOW_MOVED message to the partner (see the code in
  403. TBounceApp::MessageReceived() for the code that handles this message).  Only if
  404. we have a partner will TBounceApp::SendPosition() return TRUE.  In this case the
  405. code calls the TWindow::WindowsMoved() function, updating this window’s gap.  As
  406. BeBounce windows move about the screen everything is kept in synch.
  407. The other event of interest is generated when the user clicks on the close box of
  408. the window.  In BeBounce closing the window should also cause the application to
  409. quit.  This means that the window has to listen for the QuitRequested event:
  410.  
  411. bool TWindow::QuitRequested()
  412. {
  413.     /*
  414.      The window was asked to quit/close.  Send a message to our partner, giving
  415. him 
  416.      the ball if we’ve currently got it.
  417.     */
  418.     BMessage    *m = new BMessage(BB_GOODBYE);
  419.     if (fBall) {
  420.         fBall->Quit();
  421.         fBall = NULL;
  422.         m->AddBool("ball", TRUE);
  423.     }
  424.     my_app->SendToPartner(m);
  425.  
  426.     // Tell the app to go ahead and quit.
  427.     my_app->PostMessage(QUIT_REQUESTED);
  428.     return TRUE;
  429. }
  430.  
  431. The first thing to do when the QUIT_REQUESTED message is received is to tell our
  432. partner good-bye, passing the ball along if it is in our possession.  It would
  433. not be polite to quit with ball in hand.  Please note that the TBounceApp::
  434. SendToPartner() does the correct thing if there is no partner.  Next the window
  435. posts a message to the application telling it to quit as well.  The return value
  436. of TRUE causes to window to immediately quit.
  437. The ball
  438. The last object to describe is the ball.  Several factors influenced the design
  439. of the ball.  It is desirable to keep the ball independent from the window, yet
  440. they still need to communicate with each other.  Also, the ball periodically
  441. requires time to simulate motion.  One design that accommodates these guidelines
  442. is to construct a subclass of BLooper called TBall.  This means that the ball has
  443. its own thread and it is able to receive messages.  Here is part of the class
  444. definition:
  445.  
  446. class TBall : public BLooper {
  447. public:
  448.                                         TBall(TWindow *window, BRect bounds,
  449.                                                         BPoint center, float
  450. radius,
  451.                                                         BPoint speed);
  452.  
  453.                         void            Draw(BView *view);
  454. virtual            void            MessageReceived(BMessage *msg);
  455.  
  456.                         void            Lock();
  457.                         void            Unlock();
  458.                         void            SetGap(float start, float end);
  459.                         void            SetEnabled(bool state);
  460.         ...    // a couple other member functions
  461.  
  462. private:
  463.                         void            NextPosition(bool *hit, float *angle);
  464.                         void            Tick();
  465.  
  466.                         BLocker    fLock;
  467.                         TWindow    *fWindow;
  468.         ...    // a bunch of data members for “state” like size,
  469.             //    speed/direction, postion, etc
  470. };
  471.  
  472. Having a thread per ball would not scale to an application that had 1000 bouncing
  473. balls, but for demonstration purposes it works well.
  474. Most of the constructor for the TBall class is fairly simple, initializing the
  475. various data members.  The most interesting aspect of the constructor is that it
  476. starts the looper thread running and posts the first BB_TICK message to get the
  477. animation working:
  478.     
  479. TBall::TBall(TWindow *window, BRect bounds, BPoint center,
  480.     float radius, BPoint speed)
  481.     : fLock()
  482. {
  483.     ...    // initialize parameters like size, location, and speed
  484.     
  485.     /*
  486.      Get the looper thread rolling.  Unlike the call to BApplication::Run, which
  487. does not 
  488.      return, this call does return.
  489.     */
  490.     Run();
  491.     
  492.     // post initial message to get animation going
  493.     PostMessage(BB_TICK);
  494. }
  495.  
  496. The TBall’s MessageReceived function handles the BB_TICK message:
  497.  
  498. void TBall::MessageReceived(BMessage *msg)
  499. {
  500.     switch (msg->what) {
  501.         case BB_TICK:
  502.             Lock();
  503.             Tick();
  504.             Unlock();
  505.  
  506.             ...    // sleep for a little bit of time
  507.             
  508.             // post next message to continue animation
  509.             PostMessage(BB_TICK);
  510.             break;
  511.     }
  512. }
  513.  
  514. The Lock() and Unlock() calls shown above deserve further explanation.  The
  515. BeBounce application was designed so that the window “communicates” with the
  516. ball, not by posting messages to it, but by calling specific member functions;
  517. see the definition of the TBall class.  For example, one of those functions is
  518. TBall::SetGap():
  519.  
  520. void TBall::SetGap(float start, float end)
  521. {
  522.     Lock();
  523.     fGapStart = start;
  524.     fGapEnd = end;
  525.     Unlock();
  526. }
  527.  
  528. A consequence of this design is that two separate threads access the same data
  529. structure, so some form of synchronization is required.  The BLocker (also known
  530. as “blocker”) class provides this synchronization.  Imagine if the locking was
  531. not present.  Then in the middle of the looper thread’s calculation to determine
  532. if the ball hit that gap the window thread could come along and change the gap. 
  533. This would lead to undefined behavior.  Code in TBounceApp:: MessageReceived()
  534. locks the window for this same reason.
  535. An alternate design would have the window sending messages to the ball.  In this
  536. case, locking would not be an issue.  The act of posting messages implicitly
  537. provides the synchronization needed.  The ball’s looper thread can only handle
  538. one message at a time.  In this example the trade-off might be the latency of
  539. messages changing the “gap” position or starting and stopping the ball, with the
  540. Stop Ball menu item.  The preferred method should be determined on a case by case
  541. basis.  For educational purposes the BeBounce application uses both styles of
  542. programming.
  543. Back to the BB_TICK message.  Most of the work in animating the ball is done in
  544. TBall::Tick(), and most of that code is the math and geometry needed to animate
  545. the bouncing ball and determine when the ball either hits a wall or flies through
  546. the gap.  As little of this code is specific to the BeBox we will not go into any
  547. more detail except to show how the ball draws. 
  548.  
  549. void TBall::Tick()
  550. {
  551.     ...    // move ball to new position
  552.  
  553.     // inform the window to redraw the window.
  554.     BMessage    *msg = new BMessage(BB_DRAW);
  555.     msg->AddRect("update", updateRect);
  556.     fWindow->PostMessage(msg);
  557.  
  558.     if (hit_hole) {
  559.         /*
  560.          The ‘gap’ was hit.  So we package up the info like speed and relative
  561. location, 
  562.          which gives our partner enough information to have the ball appear in
  563. the 
  564.          correct place.
  565.         */
  566.         BMessage *msg = new BMessage(BB_HIT_HOLE);
  567.         ...    // adding info to message
  568.         fWindow->PostMessage(msg);
  569.     }
  570. }
  571.  
  572. The TBall::Tick() function moves the ball to the next position and then posts a
  573. message to the window telling it to redraw itself.  Additionally, if the ball
  574. hits the “gap” a BB_HIT_HOLE message is posted to the window:
  575. When the window receives the BB_DRAW message (in its MessageReceived function) it
  576. asks the ball to draw itself in its current location.  The drawing is done by
  577. TBall::Draw():
  578.  
  579. void TBall::Draw(BView *view)
  580. {
  581.     // The balls draws itself in the given view
  582.     Lock();
  583.     rgb_color    c = view->FrontColor();
  584.  
  585.     view->SetPenSize(1.0);
  586.     view->SetFrontColor(150, 30, 30);
  587.     view->FillArc(fCenter, fRadius, fRadius, 0.0, 360.0);
  588.     view->SetFrontColor(c);
  589.     Unlock();
  590. }
  591.  
  592. The view passed to TBall::Draw() is the same view that was added to the offscreen
  593. bitmap in the TWindow’s constructor, so the ball is being drawn offscreen.  The
  594. window then gets the TBitmapView (described earlier) to blit the offscreen image
  595. onto the screen.
  596. The code for handling the BB_HIT_HOLE message is in TWindow::MessageReceived:
  597.  
  598. void TWindow::MessageReceived(BMessage *msg)
  599. {
  600.     switch (msg->what) {
  601.             case BB_HIT_HOLE:
  602.             /*
  603.              The ball is telling us that it just hit the
  604.              hole.  So it should get sent to the partner.
  605.             */
  606.             BMessage    *m = new BMessage(BB_BALL);
  607.             fBall->Lock();
  608.             m->AddPoint("speed", msg->FindPoint("speed"));
  609.             m->AddFloat("rel_loc", msg->FindFloat("rel_loc"));
  610.             fBall->Unlock();
  611.  
  612.             // send the ‘ball’ to our partner
  613.             my_app->SendToPartner(m);
  614.  
  615.             // delete the ball from this window
  616.             fBall->Quit();
  617.             fBall = NULL;
  618.             
  619.             // redraw the window
  620.             Update();
  621.             break;
  622.         
  623.         ...    // handling of other messages
  624.     }
  625. }
  626.  
  627. The window responds to the BB_HIT_HOLE message by sending the BB_BALL message to
  628. the partner application giving it the ball.  It puts the necessary information
  629. into the message so that the partner can create the new ball in the correct
  630. position, with the correct speed and direction.  Finally, the window deletes the
  631. ball.
  632. That is how the BeBounce application works.  Obvious improvements to BeBounce
  633. would be to support more than two applications, multiple windows within the same
  634. application, and multiple balls.  Another useful addition would be a separate
  635. control window with some controls for things like ball speed.  All of these
  636. enhancements are feasible given the design of the Be operating system.  To
  637. support an arbitrary number of applications each application would maintain a
  638. list of partners, having a BMessenger object to each one.  Multiple balls in a
  639. window could either communicate with one another (for hit testing purposes)
  640. directly or use the window object as the arbitrator.
  641. Hopefully this sample application has provided a taste of developing an
  642. application for the BeBox.  The next question is: How do Be applications get
  643. built?
  644. Development Tools
  645. You write applications for the Be environment using tools supplied by one of the
  646. leading tool suppliers in the industry – Metrowerks.  Currently the preferred
  647. development environment is a Macintosh with the latest version of CodeWarrior and
  648. Be-supplied headers and libraries.  Using CodeWarrior on the Macintosh, a
  649. developer can write, compile, and link a Be application.  Of course, you have to
  650. transfer the application to the BeBox before it will run.  Since the BeBox
  651. supports ftp and includes a command-line shell (based on bash) the process of
  652. quitting your app, bringing over a new one from the Macintosh, and running it on
  653. the BeBox can be completely automated.  There is also support for using the
  654. source-level debugger on the Macintosh to debug an application while it is
  655. running on the BeBox.  Metrowerks is currently working on a port of the full
  656. CodeWarrior Integrated Development Environment to the Be operating system.  This
  657. native version of CodeWarrior, along with full technical developer documentation,
  658. will be shipped with every BeBox.
  659. Conclusion
  660. This is just a small taste of the BeBox.  We have presented an overview of the Be
  661. operating system along with some sample code to give you the general feel of Be
  662. application development.  To learn more about the Be operating system, please
  663. visit our web site, where we provide on-line versions of our developer
  664. documentation as well as information about our product and company.  We encourage
  665. developers interested in joining our support program to mail in the developer
  666. form, which you can find on the web site.
  667. Be has many plans for the future.  We are continuing to improve and add features
  668. to the Be operating system and development tools.  We are also working on future
  669. versions of the BeBox, which will include more and faster processors – including
  670. a machine with four PowerPC 604 chips for the horsepower hungry.
  671. We hope this article has succeeded in whetting your appetite for Be programming. 
  672. We look forward to seeing all the incredible applications we know developers will
  673. write for the BeBox.
  674.